/******************************************************************************
*  Copyright(c) 2021-2024 Sietium
*
*  @file:    bs-download-speed.cpp
*  @brif:    设备从系统主内存读数据性能测试
*
*  @date:    09/20/2022
*  @Author:  way.lau
*
******************************************************************************/
#include <stdlib.h>
#include <string.h>
#include "objectfactory.h"
#include "node.h"
#include "clcheck.h"
#include "cldeviceinfo.h"
#include "clevent.h"

#define TEST_ITERS 100
#define SYNC

class Bs_download_speed : public Node
{
public:
    Bs_download_speed()
    {
        nodeUint = "GBPS";
    }
    std::string name() override
    {
        return "bs-download-speed";
    }
    std::string describe() override
    {
        return "总线下载速度";
    }
    Node::NodeType  nodeType() override
    {
        return NodeTP_BsSpeed;
    }
protected:
    bool startup() override;
    bool perform() override;
    void shutdown() override;
private:
    std::vector<size_t> sizeList() const;
private:
    struct cl_handle_t {
        cl_device_id        deviceid;
        cl_context          context;
        cl_command_queue    queue;
    } handle;
};

bool Bs_download_speed::startup()
{
    memset(&handle, 0, sizeof(cl_handle_t));

    handle.deviceid = deviceinfo->deviceID();
    handle.context = clCreateContext(NULL, 1,  &handle.deviceid, NULL, NULL, NULL);
    handle.queue = clCreateCommandQueue(handle.context, handle.deviceid,
                                        CL_QUEUE_PROFILING_ENABLE,
                                        NULL);
    return true;
}

bool Bs_download_speed::perform()
{
    cl_int err;
    double fTime = 0;
    std::vector<size_t> ls = sizeList();
    clFlush(handle.queue);
    for (int i = 0; i < ls.size(); ++i) {

        const size_t dataSize = ls[i] * 1024;
        char *buff = new  char[dataSize];
        cl_mem mem = clCreateBuffer(handle.context, CL_MEM_READ_WRITE,
                                    dataSize, NULL,
                                    &err);
        CHECK_CL_ERROR(err);
        CHECK_CL_ERROR(clFinish(handle.queue));
        cl_ulong ltime  = 0;
        for (int j = 0; j < TEST_ITERS; ++j) {
            CLEvent evt;
#ifdef SYNC
            CHECK_CL_ERROR(clEnqueueWriteBuffer(handle.queue, mem, CL_TRUE, 0, dataSize,
                                                buff, 0,
                                                NULL,
                                                evt.event()));
#else
            CHECK_CL_ERROR(clEnqueueWriteBuffer(handle.queue, mem, CL_FALSE, 0, dataSize,
                                                buff, 0,
                                                NULL,
                                                evt.event()));
#endif
            // wait download finish.
            if (evt.profiling())
                ltime += (evt.endTime() - evt.submitTime());
            //clear all
            CHECK_CL_ERROR(clFinish(handle.queue));
        }
        delete []buff;
        CHECK_CL_ERROR(clReleaseMemObject(mem));
        double timeT = (dataSize * TEST_ITERS) / (ltime / 1e9f) ;
        fTime +=  timeT / 1e9f;
    }
    nodeValue = fTime / ls.size();
    return true;
}

void Bs_download_speed::shutdown()
{
    if (handle.queue) {
        CHECK_CL_ERROR(clReleaseCommandQueue(handle.queue));
        handle.queue = nullptr;
    }
    if (handle.context) {
        CHECK_CL_ERROR(clReleaseContext(handle.context));
        handle.context = nullptr;
    }
}

std::vector<size_t> Bs_download_speed::sizeList() const
{
    std::vector<size_t> list;
    size_t sizes[] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
                      32768, 65536, 131072, 262144
                     };
    const size_t nSizes =  sizeof(sizes) / sizeof (sizes[0]);
    for (int i = 0; i < nSizes; ++i) {
        if (sizes[i] * 1024 > 0.90 * deviceinfo->maxAllocSize()) {
            break;
        }
        list.push_back(sizes[i]);
    }
    return list;
}
REGISTER_OBJECT(Node, Bs_download_speed)
